#include "DefineCommon.h"
#include "ECommand.h"
#include <ECommandManager.h>
#include <cstddef>
#include <stack>

struct ECommandManagerData
{
	std::deque<ECommandPtr> m_eventQueue;
	EList<ECommandDelegate> m_listDelegate;
    //for redo undo
    std::stack<ECommandPtr> m_stackOldCmd1;
    std::stack<ECommandPtr> m_stackOldCmd2;
};

ECommandManager* ECommandManager::m_instance = NULL;

ECommandManager::~ECommandManager()
{

}

ECommandManager::ECommandManager()
{
	m_data = new ECommandManagerData;
}


ECommandManager* ECommandManager::instance()
{
	if (m_instance == NULL)
	{
		m_instance = new ECommandManager();
	}

	return m_instance;
}

void ECommandManager::processECommand()
{
    while(this->m_data->m_eventQueue.size() != 0)
    {
        for(size_t i = 0; i < this->m_data->m_listDelegate.size(); i++)
        {
            this->m_data->m_listDelegate[i](this->m_data->m_eventQueue[0]);
        }

        ECommandPtr topCmd = this->m_data->m_eventQueue.front();
        if(topCmd->getIfEnableUndo())
        {
            this->m_data->m_stackOldCmd1.push(
                    this->m_data->m_eventQueue.front()
                    );

            //clear m_stackOldCmd2, because a new cmd is pushed in m_stackOldCmd1.
            while(this->m_data->m_stackOldCmd2.empty() == false)
            {
                this->m_data->m_stackOldCmd2.pop();
            }
        }


        this->m_data->m_eventQueue.pop_front();
    }

}

void ECommandManager::processECommandUndoOnce()
{
    if(this->m_data->m_stackOldCmd1.size() == 0)
    {
        ELOG_INFO("no command need to undo");
        return;
    }

    ECommandPtr cmd = this->m_data->m_stackOldCmd1.top();
    cmd->setState(ECommand::STATE::UNDO);

    for(size_t i = 0; i < this->m_data->m_listDelegate.size(); i++)
    {
        this->m_data->m_listDelegate[i](cmd);
    }

    this->m_data->m_stackOldCmd1.pop();
    this->m_data->m_stackOldCmd2.push(cmd);
}

void ECommandManager::processECommandRedoOnce()
{
    if(this->m_data->m_stackOldCmd2.size() == 0)
    {
        ELOG_INFO("no command need to redo");
        return;
    }
    
    ECommandPtr cmd = this->m_data->m_stackOldCmd2.top();
    cmd->setState(ECommand::STATE::REDO);

    for(size_t i = 0; i < this->m_data->m_listDelegate.size(); i++)
    {
        this->m_data->m_listDelegate[i](cmd);
    }

    this->m_data->m_stackOldCmd2.pop();
    this->m_data->m_stackOldCmd1.push(cmd);
}

void ECommandManager::pushECommand( ECommandPtr event)
{
	if (event == nullptr)
	{
        ELOG_ERROR("event==null");
	}
	else
	{
		this->m_data->m_eventQueue.push_back(event);
	}
}

void ECommandManager::registDelegate(ECommandDelegate e)
{
    this->m_data->m_listDelegate.push_back(e);
}
